local NAME, addon = ...
addon.NAME = NAME
XLootGroup = addon

local L = addon.L
local me = UnitName('player')

local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or _G.RAID_CLASS_COLORS
local opt, anchor, alert_anchor
local GetLootRollItemInfo, GetLootRollItemLink, GetLootRollTimeLeft, RollOnLoot, UnitGroupRolesAssigned, print
	= GetLootRollItemInfo, GetLootRollItemLink, GetLootRollTimeLeft, RollOnLoot, UnitGroupRolesAssigned, print
local HistoryGetItem, HistoryGetPlayerInfo, HistoryGetNumItems
	= C_LootHistory.GetItem, C_LootHistory.GetPlayerInfo, C_LootHistory.GetNumItems

addon.defaults = {
	text_outline = true,
	text_time = false,
	anchor_pretty = false,
	role_icon = true,
	win_icon = false,
	show_decided = true,
	show_undecided = false,

	alert_skin = true,
	alert_alpha = 1,
	alert_scale = 1,
	alert_offset = 4,

	roll_button_size = 28,
	roll_width = 325,

	roll_anchor = {
		direction = 'up',
		visible = true,
		scale = 1.0,
		x = UIParent:GetWidth() * .85,
		y = UIParent:GetHeight() * .6
	},

	alert_anchor = {
		visible = true,
		direction = 'up',
		x = AlertFrame:GetLeft(),
		y = AlertFrame:GetTop()
	},

	track_all = true,
	track_player_roll = false,
	track_by_threshold = false,
	track_threshold = 3,

	expire_won = 20,
	expire_lost = 10
}

---------------------------------------------------------------------------
-- Skinning
---------------------------------------------------------------------------
local Skinner = {}
LibStub('X-Skin'):SetupSkins(Skinner, {
	row = {},
	item = {},
	anchor = {},
	anchor_pretty = {},
	alert = {},
	alert_item = {}
})
local skin
local skin_default = {
	name = ('|c2244dd22%s|r'):format('Smooth'),
	texture = [[Interface\AddOns\XLootGroup\textures\border_smooth]],
	bar_texture = [[Interface\AddOns\XLootGroup\textures\bar]],
	color_mod = .85,
	size = 14,
	padding = 1,
}
local skins_base = {
	anchor = { r = .4, g = .4, b = .4, a = .6, gradient = false },
	anchor_pretty = { r = .6, g = .6, b = .6, a = .8 },
	row = { gradient = false },
	item = { backdrop = false },
	alert = { gradient = false },
	alert_item = { gradient = false, backdrop = false }
}
addon.skin_default = skin_default

local function CompileSkins()
	local skins = Skinner._skins
	if XLootFrame and XLootFrame.skin then
		skin = XLootFrame.skin
	else
		skin = skin_default
	end
	for which,subskin in pairs(skins) do
		wipe(subskin)
		for k,v in pairs(skin_default) do
			subskin[k] = skin[k]
		end
		for k,v in pairs(skins_base[which]) do
			subskin[k] = v
		end
	end
	addon.skin = skin
end

addon.bars = {}

---------------------------------------------------------------------------
-- Helpers
---------------------------------------------------------------------------
-- Tack role icon on to player name and return class colors
local white = { r = 1, g = 1, b = 1 }
local dimensions = {
	HEALER = '48:64',
	DAMAGER = '16:32',
	TANK = '32:48'
}
local string_format = string.format
local function FancyPlayerName(name, class)
	local c = RAID_CLASS_COLORS[class] or white
	local role = UnitGroupRolesAssigned(name)
	if role ~= 'NONE' and opt.role_icon then
		name = string_format('\124TInterface\\LFGFRAME\\LFGROLE:12:12:-1:0:64:16:%s:0:16\124t%s', dimensions[role], name)
	end
	return name, c.r, c.g, c.b
end

local type_strings = {
	need = NEED,
	greed = GREED,
	disenchant = ROLL_DISENCHANT,
	pass = PASS
}
local rtypes = { [0] = 'pass', 'need', 'greed', 'disenchant' } -- Tekkub. Writing smaller addons than me since ever.

---------------------------------------------------------------------------
-- Frame creation
---------------------------------------------------------------------------
local rolls = {}
local mouse_focus, CreateRollFrame
do
	local sf = string.format
	-- Add a specific roll type to the tooltip
	local function RollLines(list, hid)
		for _,pid in pairs(list) do
			local name, class, rtype, roll, is_winner, is_me = HistoryGetPlayerInfo(hid, pid)
			local text, r, g, b, color = FancyPlayerName(name, class)
			if roll ~= nil then
				if is_winner then
					color = '44ff22'
				elseif is_me then
					color = 'ff2244'
				else
					color = 'CCCCCC'
				end
				GameTooltip:AddLine(sf('   |cff%s%s|r  %s', color, roll, text), r, g, b)
			else
				GameTooltip:AddLine('   '..text, r, g, b)
			end
		end
	end

	-- Add roll status or summary to tooltip
	local tneed, tgreed, tpass, trolls, tnone, ti, ts
		= {}, {}, {}, {}, {}, table.insert, table.sort
	local function rsort(a, b)
		a, b = trolls[a] or 0, trolls[b] or 0
		return a > b and true or false
	end

	local function AddTooltipLines(self, show_all, show)
		-- Locate history item
		local rollid, hid = self.rollid, 1
		local hrollid, link, players, done
		while true do
			hrollid, link, players, done = HistoryGetItem(hid)
			if not hrollid then
				return
			elseif hrollid == rollid then
				break
			end
			hid = hid+1
		end

		-- Generate player lists
		local tneed, tgreed, tpass, tnone, trolls
			= wipe(tneed), wipe(tgreed), wipe(tpass), wipe(tnone), wipe(trolls)
		for pid=1, players do
			local _, _, rtype = HistoryGetPlayerInfo(hid, pid)
			if rtype then
				if rtype == 0 then
					ti(tpass, pid)
				elseif rtype == 1 then
					ti(tneed, pid)
				elseif rtype == 2 or rtype == 3 then
					ti(tgreed, pid)
				end
				trolls[pid] = roll
			else
				ti(tnone, pid)
			end
		end

		ts(tneed, rsort)
		ts(tgreed, rsort)
		ts(tpass, rsort)

		-- Generate tooltip
		if show_all then
			GameTooltip:AddLine('.', 0, 0, 0)
		end
		if next(tneed) and (show_all or show == 1) then
			GameTooltip:AddLine(NEED, .2, 1, .1)
			RollLines(tneed, hid)
		end
		if next(tgreed) and (show_all or (show == 2 or show == 3)) then
			GameTooltip:AddLine(GREED, .1, .2, 1)
			RollLines(tgreed, hid)
		end
		if next(tpass) and (show_all or show == 0) then
			GameTooltip:AddLine(PASS, .7, .7, .7)
			RollLines(tpass, hid)
		end
		if show_all and opt.show_undecided then
			GameTooltip:AddLine(L.undecided, .7, .3, .2)
			RollLines(tnone, hid)
		end

		-- Force tooltip to refresh
		GameTooltip:Show()
		return true
	end

	---------------------------------------------------------------------------
	-- Roll buttons
	---------------------------------------------------------------------------
	local CreateRollButton
	do
		local function OnClick(self)
			RollOnLoot(self.parent.rollid, self.type)
		end
		
		local function Toggle(self, status)
			if status then
				self:Enable()
				self:SetAlpha(1)
			else
				self:Disable()
				self:SetAlpha(.6)
			end
			SetDesaturation(self:GetNormalTexture(), not status)
		end

		local function OnEnter(self)
			mouse_focus = self
			GameTooltip:SetOwner(self, 'ANCHOR_TOPLEFT')
			if not AddTooltipLines(self.parent, false, self.type) and self:IsEnabled() ~= 0 then
				GameTooltip:SetText(self.label)
				GameTooltip:Show()
			end
		end

		local function OnLeave(self)
			mouse_focus = nil
			GameTooltip:Hide()
		end

		local function SetText(self, text)
			if text and text > 0 then
				self.text:SetText(text)
			else
				self.text:SetText()
			end
		end

		local path = [[Interface\Buttons\UI-GroupLoot-%s-%s]]
		function CreateRollButton(parent, roll, label, tex, to, x, y)
			local b = CreateFrame('Button', nil, parent)
			b:SetPoint('LEFT', to, 'RIGHT', x, y)
			b:SetWidth(opt.roll_button_size)
			b:SetHeight(opt.roll_button_size)
			b:SetNormalTexture(path:format(tex, 'Up'))
			if tex ~= 'Pass' then
				b:SetHighlightTexture(path:format(tex, 'Highlight'))
				b:SetPushedTexture(path:format(tex, 'Down'))
			else
				b:SetHighlightTexture(path:format(tex, 'Up'))
				b:GetNormalTexture():SetVertexColor(0.8, 0.7, 0.7)
				b:GetHighlightTexture():SetAlpha(0.5)
			end
			b.parent = parent

			local text = b:CreateFontString(nil, 'OVERLAY')
			text:SetFont([[Fonts\FRIZQT__.TTF]], 12, 'THICKOUTLINE')
			text:SetPoint("CENTER", -x + 1, tex == 'DE' and -y +2 or -y)
			b.text = text

			b:SetScript('OnEnter', OnEnter)
			b:SetScript('OnLeave', OnLeave)
			b:SetScript('OnClick', OnClick)
			b:SetMotionScriptsWhileDisabled(true)
			b.OnEnter = OnEnter
			b.Toggle = Toggle
			b.type = roll
			b.label = label
			b.SetText = SetText

			return b
		end
	end

	---------------------------------------------------------------------------
	-- Roll frames
	---------------------------------------------------------------------------
	-- Events
	local function OnEnter(self)
		mouse_focus = self
		GameTooltip:SetOwner(self.icon_frame, 'ANCHOR_TOPLEFT', 28, 0)
		GameTooltip:SetHyperlink(self.link)
		if opt.show_decided or opt.show_undecided then
			AddTooltipLines(self, true)
		end
		if IsShiftKeyDown() then
			GameTooltip_ShowCompareItem()
		end
		if IsModifiedClick('DRESSUP') then
			ShowInspectCursor()
		else
			ResetCursor()
		end
	end

	local function OnLeave(self)
		mouse_focus = nil
		GameTooltip:Hide()
	end

	local function OnClick(self, button)
		if IsControlKeyDown() then
			DressUpItemLink(self.link)
		elseif IsShiftKeyDown() then
			ChatEdit_InsertLink(self.link)
		end
	end

	-- Status bar update
	local max = math.max
	local function BarUpdate(self)
		local parent = self.parent
		if parent.over then
			self.spark:Hide()
			self:SetValue(0)
			parent.text_time:SetText()
			return
		end
		local time = GetTime()
		-- TODO: Remove?
		if GetLootRollTimeLeft(parent.rollid) == 0 then
			local ended = parent.rollended
			if ended then
				if time - ended > 10 then
					anchor:Pop(parent)
				end
			else
				parent.rollended = time
			end
		end
		-- /TODO
		local remaining = self.expires - time
		if remaining < -4 then
			anchor:Pop(parent)
		else
			local now, length = max(remaining, -1), self.length
			self.spark:SetPoint('CENTER', self, 'LEFT', (now / length) * self:GetWidth(), 0)
			self:SetValue(now)
			self.spark:Show()
			if opt.text_time then
				if remaining >= 0 then
					parent.text_time:SetText(sf('%.0f', max(0, remaining)))
					parent.text_time:Show()
				else
					parent.text_time:Hide()
				end
			end
		end
	end

	local function Popped(self)
		rolls[self.rollid] = nil
	end

	-- Create roll frame
	function CreateRollFrame()
		-- Base frame
		local frame = CreateFrame('Button', nil, UIParent)
		frame:SetFrameLevel(anchor:GetFrameLevel())
		frame:SetHeight(24)
		frame:SetWidth(addon.opt.roll_width)
		frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
		frame:SetScript('OnEnter', OnEnter)
		frame:SetScript('OnLeave', OnLeave)
		frame:SetScript('OnClick', OnClick)
		frame.OnEnter = OnEnter
		frame.Start = Start
		frame.Popped = Popped
		
		-- Overlay (For skin border)
		local overlay = CreateFrame('frame', nil, frame)
		overlay:SetFrameLevel(frame:GetFrameLevel())
		overlay:SetAllPoints()
		Skinner:Skin(overlay, 'row')

		-- Item icon (For skin border)
		local icon_frame = CreateFrame('Frame', nil, frame)
		icon_frame:SetPoint('LEFT', 0, 0)
		icon_frame:SetWidth(28)
		icon_frame:SetHeight(28)
		Skinner:Skin(icon_frame, 'item')

		-- Item texture
		local icon = icon_frame:CreateTexture(nil, 'BACKGROUND')
		icon:SetPoint('TOPLEFT', 3, -3)
		icon:SetPoint('BOTTOMRIGHT', -3, 3)
		icon:SetTexCoord(.07,.93,.07,.93)
		
		-- Timer bar
		local bar = CreateFrame('StatusBar', nil, frame)
		bar:SetFrameLevel(frame:GetFrameLevel())
		local pad = skin.padding or skin_default.padding
		bar:SetPoint('TOPRIGHT', -pad - 3, -pad - 3)
		bar:SetPoint('BOTTOMRIGHT', -pad - 3, pad + 3)
		bar:SetPoint('LEFT', icon_frame, 'RIGHT', -pad, 0)
		bar:SetStatusBarTexture(skin.bar_texture or skin_default.bar_texture)
		bar:SetScript('OnUpdate', BarUpdate)
		bar.parent = frame
		-- Reference bar for quick re-skinning when XLoot skin changes
		table.insert(addon.bars, bar)
		
		local spark = bar:CreateTexture(nil, 'OVERLAY')
		spark:SetWidth(14)
		spark:SetHeight(38)
		spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
		spark:SetBlendMode('ADD')
		bar.spark = spark

		-- Bind text
		local bind = icon_frame:CreateFontString(nil, 'OVERLAY')
		bind:SetPoint('BOTTOM', 0, 1)
		bind:SetFont([[Fonts\FRIZQT__.TTF]], 8, 'THICKOUTLINE')

		-- Time text
		local time = icon_frame:CreateFontString(nil, 'OVERLAY')
		time:SetPoint('CENTER', 0, 2)
		time:SetFont([[Fonts\FRIZQT__.TTF]], 12, 'OUTLINE')

		-- Roll buttons
		local n = CreateRollButton(frame, 1, NEED, 'Dice', icon_frame, 3, -1)
		local g = CreateRollButton(frame, 2, GREED, 'Coin', n, 0, -2)
		local d = CreateRollButton(frame, 3, ROLL_DISENCHANT, 'DE', g, 0, 2)
		local p = CreateRollButton(frame, 0, PASS, 'Pass', d, 0, 2)

		-- Roll status text
		local status = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormal')
		status:SetHeight(16)
		status:SetJustifyH('LEFT')
		if opt.text_outline then
			local f, s = status:GetFont()
			status:SetFont(f, s, 'OUTLINE')
		end
		status:SetPoint('LEFT', icon_frame, 'RIGHT', 1, 0)
		status:SetPoint('RIGHT', p, 'RIGHT', 2, 0)

		-- Loot name/link
		local loot = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormal')
		loot:SetHeight(16)
		loot:SetJustifyH('LEFT')
		if addon.opt.text_outline then
			local f, s = loot:GetFont()
			loot:SetFont(f, s, 'OUTLINE')
		end
		loot:SetPoint('LEFT', p, 'RIGHT', 3, -1)
		loot:SetPoint('RIGHT', frame, 'RIGHT', -5, 0)

		-- Frame references
		frame.need, frame.greed, frame.disenchant, frame.pass = n, g, d, p
		frame.text_bind = bind
		frame.text_status = status
		frame.text_loot = loot
		frame.text_time = time
		frame.overlay = overlay
		frame.bar = bar
		frame.icon = icon
		frame.icon_frame = icon_frame
		frame.Update = UpdateRow

		return frame
	end
end

---------------------------------------------------------------------------
-- Group events
---------------------------------------------------------------------------
local function start(id, length, uid, ongoing)
	local icon, name, count, quality, bop, need, greed, de = GetLootRollItemInfo(id)
	local link = GetLootRollItemLink(id)
	local r, g, b = GetItemQualityColor(quality)

	local start = length
	if ongoing then
		if quality == 2 then
			length = 60000
		else
			length = 180000
		end
	end
	length, start = length/1000, start/1000

	local frame = anchor:Push()
	rolls[id] = frame

	frame.need:Show()
	frame.greed:Show()
	frame.disenchant:Show()
	frame.pass:Show()
	frame.text_status:Hide()
	frame.text_status:SetText()
	if opt.text_time then
		frame.text_time:Show()
	else
		frame.text_time:Hide()
	end

	frame.need:Toggle(need)
	frame.greed:Toggle(greed)
	frame.disenchant:Toggle(de)

	frame.need:SetText()
	frame.greed:SetText()
	frame.pass:SetText()
	frame.disenchant:SetText()

	local bar = frame.bar
	bar.length = length
	bar.expires = GetTime() + start

	frame.link = link
	frame.rollid = id
	frame.rollended = nil
	frame.quality = quality
	frame.expires = bar.expires
	frame.over = nil
	frame.have_rolled = false
	frame.lead_type = 'pass'

	frame.text_bind:SetText(bop and '|cffff4422BoP' or '')
	frame.text_loot:SetText(name)

	frame.text_loot:SetVertexColor(r, g, b)
	frame.overlay:SetBorderColor(r, g, b)
	frame.icon_frame:SetBorderColor(r, g, b)
	bar:SetStatusBarColor(r, g, b, .7)
	frame.icon:SetTexture(icon)

	bar:SetMinMaxValues(0, length)
	bar:SetValue(start)


	return frame
end

local rweights = { need = 3, greed = 2, disenchant = 2, pass = 1 }
local function changed(hid, pid)
	-- Acquire roll information and frame
	local rollid, link, players, done = HistoryGetItem(hid)
	local frame = rolls[rollid]
	if not frame or frame.rollid ~= rollid or not frame:IsShown() then
		return nil
	end
	
	-- Acquire player information
	local name, class, rtypeid, roll, winner, is_me = HistoryGetPlayerInfo(hid, pid)
	local rtype = rtypes[rtypeid]

	-- Transition or expire frame on player roll
	if is_me then
		if 	opt.track_all
			or (opt.track_player_roll and rtype ~= 'pass')
			or (opt.track_by_threshold and frame.quality >= opt.track_threshold) then
			frame.need:Hide()
			frame.greed:Hide()
			frame.disenchant:Hide()
			frame.pass:Hide()
			frame.text_status:Show()
			frame.have_rolled = true
		else
			anchor:Pop(frame)
			return
		end
	end

	-- Update post-player-roll status text
	if frame.have_rolled then
		local rtype = rtype == 'disenchant' and 'greed' or rtype
		-- Roll of leading type or higher
		if rweights[rtype] >= rweights[frame.lead_type] then
			frame.lead_type = rtype
			local bracket, mtype = 0, nil
			for i=1, players do
				local _, _, ptype, _, _, is_me = HistoryGetPlayerInfo(hid, i)
				local ptype = rtypes[ptype == 3 and 2 or ptype]
				if ptype == rtype then
					bracket = bracket + 1
				end
				if is_me then
					mtype = ptype
				end
			end

			local r, g, b = .7, .7, .7
			if mtype == rtype then
				r, g, b = .2, 1, .1
			elseif mtype and mtype ~= 0 then
				r, g, b = 1, .2, .1
			end
			frame.text_status:SetText(string_format('%s: %s', type_strings[rtype], bracket))
			frame.text_status:SetTextColor(r, g, b)
		end

	-- Update roll button counters
	else
		local bracket = 0
		for i=1, players do
			local _, _, thistype = HistoryGetPlayerInfo(hid, i)
			if thistype == rtypeid then
				bracket = bracket + 1
			end
		end
		frame[rtype]:SetText(bracket)
	end

	-- Refresh tooltip
	if frame and mouse_focus == frame then
		frame:OnEnter()
	end
end

-- Blizzard. What part of only using 0 for userdata do you not understand? =|
local tidx = { [0] = 1, [3] = 2, [2] = 2, [1] = 3 }
local function complete()
	-- Locate history item
	local hid, frame, rollid, players, done, _ = 1
	while true do
		rollid, _, players, done = HistoryGetItem(hid)
		if not rollid or (rolls[rollid] and rolls[rollid].over) then
			return
		elseif done and rolls[rollid] then
			frame = rolls[rollid]
			break
		end
		hid = hid+1
	end

	-- Active frame found
	frame.over = true
	local top_type, top_roll, top_pid = 0, 0
	for j=1, players do
		local name, class, rtype, roll, is_winner, is_me = HistoryGetPlayerInfo(hid, j)
		-- roll = roll and roll or true
		if is_winner then
			top_pid = j
			break
		elseif rtype ~= 0 and tidx[rtype] >= tidx[top_type] and (not roll or roll > top_roll) then
			top_type = rtype
			top_roll = roll
			top_pid = j
		end
	end

	-- Winner or lead
	if top_pid then
		local name, class = HistoryGetPlayerInfo(hid, top_pid)
		local player, r, g, b = FancyPlayerName(name, class)
		if opt.win_icon then
			if rtype == 'need' then
				player = [[|TInterface\Buttons\UI-GroupLoot-Dice-Up:16:16:-1:-1|t]]..player
			elseif rtype == 'greed' then
				player = [[|TInterface\Buttons\UI-GroupLoot-Coin-Up:16:16:-1:-2|t]]..player
			elseif rtype == 'disenchant' then
				player = [[|TInterface\Buttons\UI-GroupLoot-DE-Up:16:16:-1:-1|t]]..player
			end
		end
		frame.text_status:SetText(player)
		frame.text_status:SetTextColor(r, g, b)
		frame.bar.expires = GetTime()
		anchor:Expire(frame, is_me and opt.expire_won or opt.expire_lost)
	else
	-- No winner/lead
		frame.text_status:SetText(string_format('%s: %s', PASS, ALL))
		frame.text_status:SetTextColor(.7, .7, .7)
		frame.bar.expires = GetTime()
		anchor:Expire(frame, opt.expire_lost)
	end
	-- Refresh tooltip
	if frame and mouse_focus == frame then
		frame:OnEnter()
	end
end

-- To avoid a OnUpdate for every mouseoverable frame, use modifier events
local function modifier(self, modifier, state)
	if mouse_focus and MouseIsOver(mouse_focus) and mouse_focus.OnEnter then
		mouse_focus:OnEnter()
	end
end

local function AlertFrameHook(alert, link)
	-- Reskin toast
	if not alert._xskinned then
		alert._xskinned = true

		if opt.alert_skin then
			alert.Background:Hide()
			alert.IconBorder:Hide()
			alert.ItemName:SetPoint('BOTTOMRIGHT', -25, 27)

			local overlay = CreateFrame('Frame', nil, alert)
			overlay:SetPoint('TOPLEFT', 11, -11)
			overlay:SetPoint('BOTTOMRIGHT', -11, 11)
			overlay:SetFrameLevel(alert:GetFrameLevel())
			alert._overlay = overlay
			Skinner:Skin(overlay, 'alert')
			-- overlay:SetGradientColor(.5, .5, .5, .4)

			local icon_frame = CreateFrame('Frame', nil, alert)
			icon_frame:SetPoint('CENTER', alert.Icon, 'CENTER', 0, 0)
			icon_frame:SetWidth(52)
			icon_frame:SetHeight(54)
			alert._icon_frame = icon_frame
			Skinner:Skin(icon_frame, 'alert_item')
			-- icon_frame:SetGradientColor(.5, .5, .5, .4)
		end

		alert:SetAlpha(opt.alert_alpha)
		alert:SetScale(opt.alert_scale)
	end

	-- Update toast
	if opt.alert_skin then
		local _, _, rarity = GetItemInfo(link)
		local c = ITEM_QUALITY_COLORS[rarity];
		alert._overlay:SetBorderColor(c.r, c.g, c.b)
		alert._icon_frame:SetBorderColor(c.r, c.g, c.b)
	end
end

local function AlertFrameAnchorHook()
	local anchor = addon.alert_anchor
	local up, first, x, y = opt.alert_anchor.direction == 'up', true, 44, -10
	for i=1, #LOOT_WON_ALERT_FRAMES do
		local frame = LOOT_WON_ALERT_FRAMES[i]
		if frame:IsShown() then
			frame:ClearAllPoints()
			if up then
				frame:SetPoint("BOTTOM", anchor, "TOP", x, y)
			else
				frame:SetPoint("TOP", anchor, "BOTTOM", x, -y)
			end
			anchor = frame
			if first then
				first, x, y = false, 0, opt.alert_offset - 24
			end
		end
	end
end

---------------------------------------------------------------------------
-- AddOn setup and events
---------------------------------------------------------------------------
LibStub('X-Profile'):Setup(addon, 'XLootGroup_Options')
function addon:OnProfileChanged(options)
	opt = options
	addon.opt = opt
end

-- Update skins when XLoot skin changes
function addon:SkinUpdate()
	CompileSkins()
	Skinner:Reskin()
	local pad = skin.padding or skin_default.padding
	local tex = skin.bar_texture or skin_default.bar_texture
	local p, n = pad + 3, -pad - 3
	for _,bar in pairs(addon.bars) do
		bar:ClearAllPoints()
		bar:SetPoint('TOPRIGHT', n, n)
		bar:SetPoint('BOTTOMRIGHT', n, p)
		bar:SetPoint('LEFT', bar.parent.icon_frame, 'RIGHT', -pad, 0)
		bar:SetStatusBarTexture(tex)
		local link = bar.parent.link
		if link then
			local r, g, b = GetItemQualityColor(select(3, GetItemInfo(link)))
			bar.parent.overlay:SetBorderColor(r, g, b)
			bar.parent.icon_frame:SetBorderColor(r, g, b)
		end
	end

end

-- Move anchors when scale changes
function addon:ConfigSave()
	local mod = anchor:GetScale() / anchor.data.scale
	anchor:Scale(anchor.data.scale)
	anchor.data.x = anchor.data.x * mod
	anchor.data.y = anchor.data.y * mod
	anchor:Position()
	anchor:Restack()
	for _,frame in pairs(anchor.children) do
		frame:SetWidth(opt.roll_width)
		frame.need:SetWidth(opt.roll_button_size)
		frame.need:SetHeight(opt.roll_button_size)
		frame.greed:SetWidth(opt.roll_button_size)
		frame.greed:SetHeight(opt.roll_button_size)
		frame.disenchant:SetWidth(opt.roll_button_size)
		frame.disenchant:SetHeight(opt.roll_button_size)
		frame.pass:SetWidth(opt.roll_button_size)
		frame.pass:SetHeight(opt.roll_button_size)
	end
end

function addon:OnLoad()
	local XStack = LibStub('X-Stack')

	-- Create Roll anchor
	anchor = XStack:CreateStaticStack(CreateRollFrame, addon.L.anchor, opt.roll_anchor)
	anchor:SetFrameLevel(7)
	anchor:Scale(opt.roll_anchor.scale)
	addon.anchor = anchor

	-- Create alert anchor
	alert_anchor = XStack:CreateAnchor(addon.L.alert_anchor, opt.alert_anchor)
	alert_anchor:SetFrameLevel(7)
	addon.alert_anchor = alert_anchor

	-- Expire rolls
	local pip_frame = CreateFrame('Frame')
	local timer = 0
	pip_frame:SetScript('OnUpdate', function(self, elapsed)
		if timer < 1 then
			timer = timer + elapsed
		else
			timer = 0
			local time = GetTime()
			-- Extend expiration for mouseovered frames
			if mouse_focus and mouse_focus.aexpire and (mouse_focus.aexpire - time) < 5 then
				mouse_focus.aexpire = time + 5
			end
			for i, frame in ipairs(anchor.expiring) do
				if frame.aexpire and time > frame.aexpire then
					anchor:Pop(frame)
					table.remove(anchor.expiring, i)
				end
			end
			if not next(anchor.expiring) then
				self:Hide()
			end
		end
	end)
	anchor.expiring = {}
	function anchor:Expire(frame, time)
		pip_frame:Show()
		table.insert(self.expiring, frame)
		frame.aexpire = GetTime() + time
	end
	
	-- Register events
	anchor:RegisterEvent('START_LOOT_ROLL')
	anchor:RegisterEvent('LOOT_HISTORY_ROLL_CHANGED')
	anchor:RegisterEvent('LOOT_HISTORY_ROLL_COMPLETE')
	anchor:RegisterEvent('LOOT_ROLLS_COMPLETE')
	anchor:RegisterEvent('MODIFIER_STATE_CHANGED')
	anchor:SetScript('OnEvent', function(_, e, ...)
		if e == 'MODIFIER_STATE_CHANGED' then
			modifier(...)
		elseif e == 'START_LOOT_ROLL' then
			start(...)
		elseif e == 'LOOT_HISTORY_ROLL_CHANGED' then
			changed(...)
		elseif e == 'LOOT_HISTORY_ROLL_COMPLETE' or e == 'LOOT_ROLLS_COMPLETE' then
			complete(...)
		else
		end
	end)

	UIParent:UnregisterEvent("START_LOOT_ROLL")
	UIParent:UnregisterEvent("CANCEL_LOOT_ROLL")

	LibStub('X-Config'):Setup(addon, 'XLootGroupOptionPanel')
	
	-- Register as XLoot plugin for skin callback
	if XLootFrame and XLootFrame.RegisterPlugin then
		XLootFrame:RegisterPlugin(addon)
	end

	-- Compile XSkin skin sets
	CompileSkins()

	-- Skin anchor
	Skinner:Skin(anchor, opt.anchor_pretty and 'anchor_pretty' or 'anchor')
	Skinner:Skin(alert_anchor, opt.anchor_pretty and 'anchor_pretty' or 'anchor')

	-- Find and show active rolls
	if GetNumGroupMembers() > 0 and (GetLootMethod() == 'group' or GetLootMethod() == 'needbeforegreed') then
		for i=1,300 do
			local time = GetLootRollTimeLeft(i)
			if time > 0 and time <  300000 then
				start(i, time, true)
			end
		end
	end

	hooksecurefunc('LootWonAlertFrame_SetUp', AlertFrameHook)
	hooksecurefunc('AlertFrame_SetLootWonAnchors', AlertFrameAnchorHook)
end

function addon.ToggleAnchors() 
	if anchor:IsShown() then
		anchor:Hide()
		alert_anchor:Hide()
	else
		anchor:Show()
		alert_anchor:Show()
	end
end

-- Slash command for toggling anchors
local function option_handler(msg)
	if msg == 'reset' then
		addon:ResetProfile()
		anchor:Position()
		alert_anchor:Position()
	elseif msg == 'opt' or msg == 'options' then
		addon:ShowOptions()
	else
		addon.ToggleAnchors()
	end
end
SLASH_XLOOTGROUP1 = '/xlg'
SLASH_XLOOTGROUP2 = '/xlootgroup'
SlashCmdList['XLOOTGROUP'] = option_handler

